package com.ruoyi.common.utils.file;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.ruoyi.common.config.AliyunOssConfig;
import com.ruoyi.common.exception.file.FileUploadException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;

/**
 * 阿里云OSS文件上传工具类
 * 
 * @author ruoyi
 */
@Component
public class AliyunOssUtils
{
    @Autowired
    private AliyunOssConfig aliyunOssConfig;

    /**
     * 上传文件到OSS
     * 
     * @param file 上传的文件
     * @return 文件访问URL
     * @throws Exception
     */
    public String uploadFile(MultipartFile file) throws Exception
    {
        if (file == null || file.isEmpty())
        {
            throw new FileUploadException("上传文件不能为空");
        }

        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(
            aliyunOssConfig.getEndpoint(),
            aliyunOssConfig.getAccessKeyId(),
            aliyunOssConfig.getAccessKeySecret()
        );

        try
        {
            // 生成文件名
            String fileName = generateFileName(file.getOriginalFilename());
            
            // 上传文件
            PutObjectRequest putObjectRequest = new PutObjectRequest(
                aliyunOssConfig.getBucketName(),
                fileName,
                file.getInputStream()
            );
            
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            
            // 阿里云OSS上传成功时，result不为null即可认为上传成功
            // 不需要检查Response.isSuccessful()，因为某些情况下可能为false但实际已上传成功
            if (result != null)
            {
                // 返回文件访问URL
                return getFileUrl(fileName);
            }
            else
            {
                throw new FileUploadException("文件上传失败");
            }
        }
        finally
        {
            // 关闭OSSClient
            if (ossClient != null)
            {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 上传本地文件到OSS
     * 
     * @param localFilePath 本地文件路径
     * @return 文件访问URL
     * @throws Exception
     */
    public String uploadLocalFile(String localFilePath) throws Exception
    {
        if (StringUtils.isEmpty(localFilePath))
        {
            throw new FileUploadException("本地文件路径不能为空");
        }

        File localFile = new File(localFilePath);
        if (!localFile.exists())
        {
            throw new FileUploadException("本地文件不存在: " + localFilePath);
        }

        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(
            aliyunOssConfig.getEndpoint(),
            aliyunOssConfig.getAccessKeyId(),
            aliyunOssConfig.getAccessKeySecret()
        );

        try
        {
            // 生成文件名
            String fileName = generateFileName(localFile.getName());
            
            // 上传文件
            PutObjectRequest putObjectRequest = new PutObjectRequest(
                aliyunOssConfig.getBucketName(),
                fileName,
                localFile
            );
            
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            
            // 阿里云OSS上传成功时，result不为null即可认为上传成功
            // 不需要检查Response.isSuccessful()，因为某些情况下可能为false但实际已上传成功
            if (result != null)
            {
                // 返回文件访问URL
                return getFileUrl(fileName);
            }
            else
            {
                throw new FileUploadException("文件上传失败");
            }
        }
        finally
        {
            // 关闭OSSClient
            if (ossClient != null)
            {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 上传字节数组到OSS
     * 
     * @param data 字节数组
     * @param fileName 文件名
     * @return 文件访问URL
     * @throws Exception
     */
    public String uploadBytes(byte[] data, String fileName) throws Exception
    {
        if (data == null || data.length == 0)
        {
            throw new FileUploadException("上传数据不能为空");
        }

        if (StringUtils.isEmpty(fileName))
        {
            throw new FileUploadException("文件名不能为空");
        }

        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(
            aliyunOssConfig.getEndpoint(),
            aliyunOssConfig.getAccessKeyId(),
            aliyunOssConfig.getAccessKeySecret()
        );

        try
        {
            // 生成文件名
            String ossFileName = generateFileName(fileName);
            
            // 上传文件
            PutObjectRequest putObjectRequest = new PutObjectRequest(
                aliyunOssConfig.getBucketName(),
                ossFileName,
                new ByteArrayInputStream(data)
            );
            
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            
            // 阿里云OSS上传成功时，result不为null即可认为上传成功
            // 不需要检查Response.isSuccessful()，因为某些情况下可能为false但实际已上传成功
            if (result != null)
            {
                // 返回文件访问URL
                return getFileUrl(ossFileName);
            }
            else
            {
                throw new FileUploadException("文件上传失败");
            }
        }
        finally
        {
            // 关闭OSSClient
            if (ossClient != null)
            {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 从OSS删除文件
     * 
     * @param fileName 文件名（包含路径）
     * @return 是否删除成功
     */
    public boolean deleteFile(String fileName)
    {
        if (StringUtils.isEmpty(fileName))
        {
            return false;
        }

        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(
            aliyunOssConfig.getEndpoint(),
            aliyunOssConfig.getAccessKeyId(),
            aliyunOssConfig.getAccessKeySecret()
        );

        try
        {
            // 删除文件
            ossClient.deleteObject(aliyunOssConfig.getBucketName(), fileName);
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
        finally
        {
            // 关闭OSSClient
            if (ossClient != null)
            {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 检查文件是否存在
     * 
     * @param fileName 文件名（包含路径）
     * @return 文件是否存在
     */
    public boolean doesObjectExist(String fileName)
    {
        if (StringUtils.isEmpty(fileName))
        {
            return false;
        }

        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(
            aliyunOssConfig.getEndpoint(),
            aliyunOssConfig.getAccessKeyId(),
            aliyunOssConfig.getAccessKeySecret()
        );

        try
        {
            // 检查文件是否存在
            return ossClient.doesObjectExist(aliyunOssConfig.getBucketName(), fileName);
        }
        catch (Exception e)
        {
            return false;
        }
        finally
        {
            // 关闭OSSClient
            if (ossClient != null)
            {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 获取文件访问URL
     * 
     * @param fileName 文件名（包含路径）
     * @return 文件访问URL
     */
    public String getFileUrl(String fileName)
    {
        if (StringUtils.isEmpty(fileName))
        {
            return null;
        }
        
        return "https://" + aliyunOssConfig.getBucketName() + "." + aliyunOssConfig.getEndpoint() + "/" + fileName;
    }

    /**
     * 生成带签名的文件访问URL（临时访问）
     * 
     * @param fileName 文件名（包含路径）
     * @param expireTime 过期时间（秒）
     * @return 带签名的文件访问URL
     */
    public String getSignedUrl(String fileName, long expireTime)
    {
        if (StringUtils.isEmpty(fileName))
        {
            return null;
        }

        // 创建OSSClient实例
        OSS ossClient = new OSSClientBuilder().build(
            aliyunOssConfig.getEndpoint(),
            aliyunOssConfig.getAccessKeyId(),
            aliyunOssConfig.getAccessKeySecret()
        );

        try
        {
            // 生成带签名的URL
            Date expiration = new Date(System.currentTimeMillis() + expireTime * 1000);
            URL url = ossClient.generatePresignedUrl(aliyunOssConfig.getBucketName(), fileName, expiration);
            return url.toString();
        }
        catch (Exception e)
        {
            return null;
        }
        finally
        {
            // 关闭OSSClient
            if (ossClient != null)
            {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 生成文件名
     * 
     * @param originalFileName 原始文件名
     * @return 生成的文件名
     */
    private String generateFileName(String originalFileName)
    {
        if (StringUtils.isEmpty(originalFileName))
        {
            originalFileName = "unknown";
        }

        // 获取文件扩展名
        String extension = "";
        int lastDotIndex = originalFileName.lastIndexOf(".");
        if (lastDotIndex > 0)
        {
            extension = originalFileName.substring(lastDotIndex);
        }

        // 生成唯一文件名：file/ + 日期路径 + UUID + 扩展名
        String datePath = DateUtils.datePath();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        
        return "file/" + datePath + "/" + uuid + extension;
    }

    /**
     * 从URL中提取文件名
     * 
     * @param url 文件URL
     * @return 文件名
     */
    public String extractFileNameFromUrl(String url)
    {
        if (StringUtils.isEmpty(url))
        {
            return null;
        }

        try
        {
            // 移除域名部分，获取文件路径
            String filePath = url.replace("https://" + aliyunOssConfig.getBucketName() + "." + aliyunOssConfig.getEndpoint() + "/", "");
            return filePath;
        }
        catch (Exception e)
        {
            return null;
        }
    }
}
